﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;

namespace StuffNET.Math
{
    public class Coordinate
    {
        /// <summary>
        /// Gets or sets the X value of the coordinate
        /// </summary>
        public double X { get; set; }

        /// <summary>
        /// Gets or sets the Y value of the coordinate
        /// </summary>
        public double Y { get; set; }

        /// <summary>
        /// Generates a System.Drawing.Point structure
        /// </summary>
        public Point Point
        {
            get
            {
                return new Point((int)X, (int)Y);
            }
        }

        /// <summary>
        /// Gets a coordinate with x and y set to double.NegativeInfinity
        /// </summary>
        public static Coordinate Infinity { get { return new Coordinate(double.NegativeInfinity, double.NegativeInfinity); } }

        /// <summary>
        /// Gets a Coordinate with x = 0 and y = 0
        /// </summary>
        public static Coordinate Zero { get { return new Coordinate(0.0, 0.0); } }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x">The x value of the coordinate</param>
        /// <param name="y">The y value of the coordinate</param>
        public Coordinate(int x, int y)
        {
            X = x;
            Y = y;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="x">The x value of the coordinate</param>
        /// <param name="y">The y value of the coordinate</param>
        public Coordinate(double x, double y)
        {
            X = x;
            Y = y;
        }

        /// <summary>
        /// Rotates an array of coordinates.
        /// </summary>
        /// <param name="coordinates">The coordinates that should be rotated</param>
        /// <param name="pivot">The pivot of the rotation</param>
        /// <param name="angle">The angle (in radian) of the rotation. Positive values cause a clockwise rotation, negative a counterclockwise rotation.</param>
        /// <returns></returns>
        public static Coordinate[] RotateCoordinatesAroundPivot(Coordinate[] coordinates, Coordinate pivot, double angle)
        {
            List<Coordinate> coords = new List<Coordinate>();

            for (int i = 0; i < coordinates.Length; i++)
            {
                double currentAngle = Math.Geometry.GetAngle(pivot.X, pivot.Y, coordinates[i].X, coordinates[i].Y);
                double disToPivot = Math.Geometry.GetDistance(pivot.X, pivot.Y, coordinates[i].X, coordinates[i].Y);

                Coordinate newCoord = new Coordinate(pivot.X + (System.Math.Cos(currentAngle + angle) * disToPivot), pivot.Y + (System.Math.Sin(currentAngle + angle) * disToPivot));
                coords.Add(newCoord);
            }

            return coords.ToArray();
        }

        /// <summary>
        /// Calculates the center of all given coordninates
        /// </summary>
        /// <param name="coordinates">The coordinates the center should be calculated from</param>
        /// <returns>A Coordinate pointing to the calculated center</returns>
        public static Coordinate GetCenter(Coordinate[] coordinates)
        {
            if (coordinates.Length == 0)
                return null;

            double totalX = 0, totalY = 0;
            foreach (Coordinate c in coordinates)
            {
                totalX += c.X;
                totalY += c.Y;
            }

            return new Coordinate(totalX / coordinates.Length, totalY / coordinates.Length);
        }

        public override string ToString()
        {
            return "X: " + string.Format("{2:F}", X) + " | Y: " + string.Format("{2:F}", Y);
        }

        public override bool Equals(object obj)
        {
            if (obj is Coordinate)
            {
                Coordinate c = obj as Coordinate;
                double dis = Geometry.GetDistance(this.X, this.Y, c.X, c.Y);
                if (dis <= 0.0001)
                    return true;
                else if (X == c.X && Y == c.Y)
                    return true;
            }
            return base.Equals(obj);
        }
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
        public static bool operator ==(Coordinate c1, Coordinate c2)
        {
            if (System.Object.ReferenceEquals(c1, c2))
                return true;
            if (((object)c1 == null) || ((object)c2 == null))
                return false;
            return c1.X == c2.X && c1.Y == c2.Y;
        }
        public static bool operator !=(Coordinate c1, Coordinate c2)
        {
            return !(c1 == c2);
        }

        public static implicit operator Point(Coordinate c)
        {
            return c.Point;
        }

        public static implicit operator Coordinate(Point p)
        {
            return new Coordinate(p.X, p.Y);
        }
    }
}
